#include "clientFunc.h"

int recvn(int sockfd, void *buf, int length)
{
    int total = 0; // 记录已经收到的数据的长度
    char *p = (char *)buf;
    while (total < length)
    {
        ssize_t sret = recv(sockfd, p + total, length - total, 0);
        total += sret;
    }
    return total;
}
int recvFile(int sockfd)
{
    train_t train;
    char filename[1024] = {0};
    int length;
    // 接收文件名
    recvn(sockfd, &length, sizeof(length));
    recvn(sockfd, filename, length);
    // 接收要获取的文件的总大小
    off_t filesize;
    recvn(sockfd, &length, sizeof(length));
    recvn(sockfd, &filesize, length);
    printf("filesize = %ld\n", filesize);
    // 打开文件
    int fd = open(filename, O_RDWR | O_CREAT, 0666);
    // 获取已下载的文件的大小，如果是空文件，获取的是0
    off_t downloaded_size = lseek(fd, 0, SEEK_END);
    // 定位文件的读写位置
    if (downloaded_size > 0)
    {
        int ret = lseek(fd, downloaded_size, SEEK_SET);
        ERROR_CHECK(ret, -1, "lseek");
    }
    // 告诉服务端已下载文件的大小
    train.length = sizeof(downloaded_size);
    memcpy(train.data, &downloaded_size, train.length);
    send(sockfd, &train, sizeof(int) + train.length,MSG_NOSIGNAL);
    // 接收文件块并写入本地文件
    off_t total_bytes_read = downloaded_size;
    while (1)
    {
        recvn(sockfd, &train.length, sizeof(train.length));
        if (train.length != 1000)
        {
            printf("length = %d\n", length);
        }
        if (train.length == 0)
        {
            break;
        }
        recvn(sockfd, train.data, train.length);
        write(fd, train.data, train.length);
        print_progress_bar(total_bytes_read, filesize);
    }
    return 0;
}

int transFile(int sockfd, const char *fileName, int fileNameLength)
{
    char filename[1024] = {0};
    memcpy(filename, fileName, strlen(fileName) - 1);
    printf("filename=%s\n", filename);
    train_t train;
    // 发送文件名
    train.length = strlen(filename);
    memcpy(train.data, filename, train.length);
    send(sockfd, &train, sizeof(int) + train.length, MSG_NOSIGNAL);
    // 打开文件
    int fd = open(fileName, O_RDWR);
    // 获取文件总大小
    struct stat statbuf;
    fstat(fd, &statbuf);

    off_t filesize = statbuf.st_size;
    // 发文件的总大小
    train.length = sizeof(statbuf.st_size);
    memcpy(train.data, &statbuf.st_size, train.length);
    send(sockfd, &train, sizeof(int) + train.length, MSG_NOSIGNAL);

    // 将要上传的文件映射到内存
    char *p = (char *)mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    ERROR_CHECK(p, MAP_FAILED, "mmap");
    off_t totalsize =0;
    while (totalsize < statbuf.st_size)
    {
        if (statbuf.st_size - totalsize > 1000)
        {
            // 剩余未发送的内容超过1000的情况
            train.length = 1000;
        }
        else
        {
            // 剩余内容不足1000的情况
            train.length = statbuf.st_size - totalsize;
        }
        send(sockfd, &train.length, sizeof(train.length), MSG_NOSIGNAL); // 发火车头
        send(sockfd, p + totalsize, train.length, MSG_NOSIGNAL);         // 火车车厢在mmap映射区
        totalsize += train.length;
    }

    // 补发一个空火车
    train.length = 0;
    send(sockfd, &train.length, sizeof(train.length), MSG_NOSIGNAL); // 发火车头
    munmap(p, statbuf.st_size);
    return 0;
}

void print_by_bytes(const char *s, size_t num_bytes)
{
    char *buffer = (char *)malloc(num_bytes + 1); // 为缓冲区分配内存
    if (buffer == NULL)
    {
        fprintf(stderr, "Error: Failed to allocate memory.\n");
        return;
    }

    // 使用 snprintf 将格式化字符串写入缓冲区
    snprintf(buffer, num_bytes + 1, "%s", s);

    // 确保输出的字符串不会超过指定的字节数
    if (strlen(buffer) > num_bytes)
    {
        buffer[num_bytes] = '\0';
    }

    printf("%s\n", buffer);

    // 释放缓冲区内存
    free(buffer);
}

void print_progress_bar(off_t current_size, off_t total_size)
{
    int progress = (int)((double)current_size / (double)total_size * PROGRESS_BAR_WIDTH);
    printf("\r[");
    for (int i = 0; i < progress; i++)
    {
        printf("=");
    }
    for (int i = progress; i < PROGRESS_BAR_WIDTH; i++)
    {
        printf(" ");
    }
    printf("] %3.2f%%", (double)current_size / (double)total_size * 100.0);
    fflush(stdout);
}
